using System;
using System.IO;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using ICustomAttributeProvider = Mono.Cecil.ICustomAttributeProvider;

namespace BruteMerge.Merger
{
    internal class AssemblyProcessor
    {
        private readonly AssemblyDefinition _assembly;



        private AssemblyProcessor(AssemblyDefinition assembly)
        {
            _assembly = assembly;
        }

        public AssemblyProcessor(Stream stream, IAssemblyResolver assemblyResolver)
            : this(AssemblyDefinition.ReadAssembly(stream, new ReaderParameters { AssemblyResolver = assemblyResolver }))
        {
        }

        public AssemblyProcessor(string fileName, IAssemblyResolver assemblyResolver)
            : this(AssemblyDefinition.ReadAssembly(fileName, new ReaderParameters { AssemblyResolver = assemblyResolver }))
        {
        }


        private void DefineAttributeInternal<TAttribute>(ICustomAttributeProvider attributeProvider, params object[] args)
            where TAttribute : Attribute
        {
            var attributeType = typeof (TAttribute);
            var attributeConstructorTypes = args.Select(a => a.GetType()).ToArray();
            var attributeCtor = attributeType.GetConstructor(attributeConstructorTypes);

            var attributeCtorReference = _assembly.MainModule.Import(attributeCtor);
            var attriibute = new CustomAttribute(attributeCtorReference);

            foreach (var arg in args)
            {
                var typeReference = _assembly.MainModule.Import(arg.GetType());
                attriibute.ConstructorArguments.Add(new CustomAttributeArgument(typeReference, arg));
            }

            attributeProvider.CustomAttributes.Add(attriibute);
        }

        public void DefineAssemblyAttribute<TAttribute>(params object[] args) 
            where TAttribute : Attribute
        {
            DefineAttributeInternal<TAttribute>(_assembly, args);
        }

        public void AddResource(string name, byte[] data)
        {
            _assembly.MainModule.Resources.Add(new EmbeddedResource(name, ManifestResourceAttributes.Public, data));
        }

        public void SetConsoleCubsystem()
        {
            _assembly.MainModule.Kind = ModuleKind.Console;
        }

        public void Save(string fileName)
        {
            _assembly.Write(fileName);
        }
    }
}